﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Tomahawk.Runtime.Components
{
    public class FramerateParameters : OverlayParameters
    {
    }

    /// <summary>
    /// A reusable component for tracking the frame rate.
    /// </summary>
    public class FramerateComponent : OverlayComponent
    {
        #region Instance
        private static FramerateComponent instance = null;
        public static FramerateComponent Instance { get { return instance; } }
        #endregion

        #region Fields
        int gameUpdateRate = 0;
        int gameUpdateCounter = 0;

        int gameFrameRate = 0;
        int gameFrameCounter = 0;
        TimeSpan elapsedGameTime = TimeSpan.Zero;

        int realUpdateRate = 0;
        int realUpdateCounter = 0;

        int realFrameRate = 0;
        int realFrameCounter = 0;
        TimeSpan elapsedRealTime = TimeSpan.Zero;

        int minFps = 0;
        TimeSpan minFpsTimeStamp = TimeSpan.Zero;

        int maxFps = 0;
        TimeSpan maxFpsTimeStamp = TimeSpan.Zero;

        int minUps = 0;
        TimeSpan minUpsTimeStamp = TimeSpan.Zero;

        int maxUps = 0;
        TimeSpan maxUpsTimeStamp = TimeSpan.Zero;
        #endregion

        #region Initialization
        public static void Register(Game game, ref FramerateParameters parameters)
        {
            if (parameters.enabled)
            {
                Vector2 basePos = Vector2.Zero;
                basePos.X = parameters.X;
                basePos.Y = parameters.Y;
                instance = new FramerateComponent(basePos, game);

                game.Components.Add(instance);
            }
        }
        
        protected FramerateComponent(Vector2 basePos, Game game)
            : base(basePos, game)
        {
        }
        #endregion

        #region Update
        protected override void OnUpdate(GameTime gameTime)
        {
            gameUpdateCounter++;
            realUpdateCounter++;

            elapsedGameTime += gameTime.ElapsedGameTime;
            elapsedRealTime += gameTime.ElapsedRealTime;

            if (elapsedGameTime > TimeSpan.FromSeconds(1))
            {
                elapsedGameTime -= TimeSpan.FromSeconds(1);

                gameFrameRate = gameFrameCounter;
                gameFrameCounter = 0;

                gameUpdateRate = gameUpdateCounter;
                gameUpdateCounter = 0;
            }

            if (elapsedRealTime > TimeSpan.FromSeconds(1))
            {
                elapsedRealTime -= TimeSpan.FromSeconds(1);

                realFrameRate = realFrameCounter;
                realFrameCounter = 0;

                realUpdateRate = realUpdateCounter;
                realUpdateCounter = 0;
            }

            if (gameTime.ElapsedGameTime > TimeSpan.FromSeconds(0))
            {
                int instantUps = (int)(1.0 / gameTime.ElapsedGameTime.TotalSeconds);

                if (gameTime.TotalGameTime - minUpsTimeStamp > TimeSpan.FromSeconds(1) || minUps > instantUps)
                {
                    minUps = instantUps;
                    minUpsTimeStamp = gameTime.TotalGameTime;
                }

                if (gameTime.TotalGameTime - maxUpsTimeStamp > TimeSpan.FromSeconds(1) || maxUps < instantUps)
                {
                    maxUps = instantUps;
                    maxUpsTimeStamp = gameTime.TotalGameTime;
                }
            }
        }
        #endregion

        #region Draw
        protected override void OnDraw2D(GameTime gameTime)
        {
            gameFrameCounter++;
            realFrameCounter++;

            if (gameTime.ElapsedGameTime > TimeSpan.FromSeconds(0))
            {
                int instantFps = (int) (1.0 / gameTime.ElapsedGameTime.TotalSeconds);

                if (gameTime.TotalGameTime - minFpsTimeStamp > TimeSpan.FromSeconds(1) || minFps > instantFps)
                {
                    minFps = instantFps;
                    minFpsTimeStamp = gameTime.TotalGameTime;
                }

                if (gameTime.TotalGameTime - maxFpsTimeStamp > TimeSpan.FromSeconds(1) || maxFps < instantFps)
                {
                    maxFps = instantFps;
                    maxFpsTimeStamp = gameTime.TotalGameTime;
                }
            }

            string time = string.Format("Time: {0} ({1})", gameTime.TotalGameTime, gameTime.TotalRealTime);
            string fps  = string.Format("FPS: {0} ({1}) [{2} - {3}]", gameFrameRate, realFrameRate, minFps, maxFps);
            string ups = string.Format("UPS: {0} ({1}) [{2} - {3}]", gameUpdateRate, gameFrameRate, minUps, maxUps);

            Color timeColor = gameTime.IsRunningSlowly ? Color.Red : Color.White;
            DrawString(0, 0, time, ref timeColor);

            Color fpsColor = gameFrameRate < 25 ? Color.Red : Color.White;
            DrawString(0, 10, fps, ref fpsColor);

            Color upsColor = gameUpdateRate < 25 ? Color.Red : Color.White;
            DrawString(0, 20, ups, ref upsColor);
        }

        protected override void OnDraw3D(GameTime gameTime)
        {
        }

        #endregion
    }
}
